/**
 *   Main implementation of Minilisim Game
 *
 *  * 
 * 
 * Date 2017/2/9
 * Author Jason
 * 
 */

import Game from './lib/gameEngine.js';
import Event from './lib/observer.js';
import {Sprite,SpriteAnimator,ImagePainter} from './lib/sprites';
import * as Behavior from './lib/behavior.js' ;
import * as Arc from './common/arc.js';
import * as Animate from './common/animate';

var game =new Game('miniGame','canvas'),
    loading=false, // loading flag
    //Score
    score=0,
    lastScore=0,
    lastScoreUpdate=undefined,
    
    //Over
    gameOver=false,
    gameStart=false,

    //Key Listeners
    lastKeyListenerTime=0, //For throttling arrow keys

    //Scrolling the background........
    translateDelta=3, // 1 0.025
    translateOffset=0,

    // Lives......................................................
    livesLeft = 1,

    //random color , 5s change once?(first thought)
    // It needs to be an array, store the color of bottom rect
    // Length depends of the RECT_NUM
    // And contains every 
    // draw前面的4个Rect
    ranColor=[],
//draw后面4个Rect
    backColor=[],
    //实际的用于检测对应的颜色数组
    currentLedgeColor=[],
    //store all colors
    stageColor=[],
    //color that you earn,also means the score!
    //临时数组用于存储当前拥有的颜色，当创建好statge之后就存放了
    //所有颜色
    myColor=[], //score?
    //realMyObj存储了游戏中得到的气球，lineBall的 left距离和颜色！
    //在下一局游戏时候要清除！
    realMyObj=[],//realMyColor=[],
    countRect=0,

//临时数组，存放剩下的新颜色
    leftColor=[],

    //High score
    HIGH_SCORES_DISPLAYED=10,
    //Canvas
    CANVAS_WIDTH=game.context.canvas.width,
    CANVAS_HEIGHT=game.context.canvas.height,

        //Sun Constants
    SUN_TOP=CANVAS_HEIGHT*0.1,//CANVAS_WIDTH/2,
    SUN_LEFT=CANVAS_WIDTH*0.8,
    SUN_RADIUS=30,

    //Rounded Rect
    TOTAL_RECT=150,
    RECT_NUM=4,//RECT_NUM
    RECT_STROKE_STYLE= '#0099FF',
   //Accurate fill style  should be generated by a algorithm  
    FILL_STYLES=['red','blue','green','chocolate',
        'purple','orange','gray','magenta',
        'PowderBlue','Brown','BlueViolet'],
    //4 means 
    RECT_WIDTH=CANVAS_WIDTH/(RECT_NUM),// RECT_NUM/2
    RECT_HEIGHT=CANVAS_HEIGHT/3,

    CORNER_RADIUS=12,
    BALL_LAUNCH_LEFT=CANVAS_WIDTH/3,
    BALL_HEIGHT=CANVAS_WIDTH*0.08,
    BALL_WIDTH=BALL_HEIGHT,
    BALL_STROKE_STYLE='LightGrey',
    BALL_FILL_STYLES='LightCoral'
    ;
    let BALL_LAUNCH_TOP=CANVAS_HEIGHT-RECT_HEIGHT-2*BALL_HEIGHT;
let context=game.context;
//滚动实现： 先计算出绘制图层需要对坐标系进行平移的距离，然后保存
//绘图环境对象的状态，平移坐标原点，并将每个图层之中的物体绘制出来，
//最后回复绘图环境对象
let scrollBackground=function(){
    //translate the canvas;
        //console.log('run background');
        context.save();
        //%game.context.canvas.witdh;
        translateOffset=translateOffset<context.canvas.width?
                            (translateOffset+translateDelta):0;
       // console.log(translateOffset);
  
        context.translate(-translateOffset,0);
        
// Two image actually ! Use one to cover the gap of another 
// 两张图片(两个图形)，一个去弥补另一个
        // paintNearCloud(context,120,20);
        // paintNearCloud(context,context.canvas.width+120,20);
        //console.log(currentLedgeColor);
        //这两个判断需要gameStart为true的时候才对！
        if(translateOffset===CANVAS_WIDTH&&gameStart){
            changeColor();
        }
         //update the current Color array which relects the ledgeColor
        updateCurrentColor(translateOffset);
        //Count 
        if(translateOffset%RECT_WIDTH===0&&translateOffset>=RECT_WIDTH&&gameStart){
            countRect++;   
            //console.log(countRect);
            updateMyRealColor();
            //every RECT_WIDTH we update the rect 's left
            resetLeft();
        }

         //Paint Bottom 
        paintBottomRect(context,0);

        //It must be context.canvas.width ！if context.canvas.width-50
        //then it will be 50 gap! 
        paintBottomRectOff(context,context.canvas.width); //paintBottomRect
        if(gameStart){
            updateLedgeLeft(translateDelta);
        }
        //此处更新ledgeColor
        updateLedgeColor();
        context.restore();
    },

    //Paint methods
    //每一个离屏canvas的更新其实不会在浏览器上
    drawOnOffScreen=function(offcanvas){
        offcanvas.width=CANVAS_WIDTH;
        offcanvas.height=CANVAS_HEIGHT;
        var offContext=offcanvas.getContext('2d');
        paintNearCloud(offContext,120,20);
        paintNearCloud(offContext,offContext.canvas.width+120,20);
                 //Paint Bottom 
        paintBottomRect(offContext,0);

        //It must be context.canvas.width ！if context.canvas.width-50
        //then it will be 50 gap! 
        paintBottomRectOff(offContext,offContext.canvas.width); //paintBottomRect
    },
    
    paintBottomRect=function(context,offset){
        let y=context.canvas.height-RECT_HEIGHT;//x
        //change too fast , 
        //let ranColor=generateRandom();
        //console.log('paintRect'); hundreds times~!
        for( let i=0;i<RECT_NUM;i++){//RECT_NUM*2
            let x=offset+RECT_WIDTH*i;
            Arc.drawRoundedRect(RECT_STROKE_STYLE,ranColor[i],
                                            x,y,RECT_WIDTH,
                                            RECT_HEIGHT,CORNER_RADIUS);
        }

    },    
    paintBottomRectOff=function(context,offset){
        let y=context.canvas.height-RECT_HEIGHT;//x
        //change too fast , 
        //let ranColor=generateRandom();
        //console.log('paintRect'); hundreds times~!
        // let tmpRect=Behavior.fallOnLedge.ledgeRect;
        for( let i=0;i<RECT_NUM;i++){//RECT_NUM*2
            let x=offset+RECT_WIDTH*i;
            Arc.drawRoundedRect(RECT_STROKE_STYLE,backColor[i],
                                            x,y,RECT_WIDTH,
                                            RECT_HEIGHT,CORNER_RADIUS);
        }

    },    
    paintSun = function (context) {
       context.save();

       context.strokeStyle = 'LightSlateGray';
       context.fillStyle = 'LightGoldenRodYellow';
       context.lineWidth = 1;

       context.beginPath();
       context.arc(SUN_LEFT, SUN_TOP, SUN_RADIUS, 0, Math.PI*2, true);
       context.fill();
       context.stroke();

       context.restore();
    },
/*    paintFarCloud=function(context,x,y){
        context.save();
        context.lineWidth=0.5; 
        context.strokeStyle='rgba(100,140,230,0,0.8)';
        context.fillStyle='rgba(255,255,255,0.4)';
        context.beginPath();
        context.moveTo(x+102,y+91);
        //draw Adds a quadratic Bézier curve to the current path.
        context.quadraticCurveTo(x+180, y+110, x+250, y+90);
        context.quadraticCurveTo(x+312, y+87, x+279, y+60);
        context.quadraticCurveTo(x+321, y+20, x+265, y+20);
        context.quadraticCurveTo(x+219, y+4, x+171, y+23);
        context.quadraticCurveTo(x+137, y+5, x+104, y+18);
        context.quadraticCurveTo(x+57, y+23, x+79, y+48);
        context.quadraticCurveTo(x+57, y+74, x+104, y+92);
        context.closePath();
        context.stroke();
        context.fill();
        context.restore();
    },*/

    paintNearCloud=function(context,x,y){
       context.save();
       context.lineWidth=0.5;
       context.strokeStyle='rgba(100, 140, 230, 0, 0.8)';
       context.fillStyle='rgba(255,255,255,0.4)';
       context.beginPath();

       context.fillStyle='rgba(255,255,255,0.7)';

       context.moveTo(x+364, y+37);
       context.quadraticCurveTo(x+426, y+28, x+418, y+72);
       context.quadraticCurveTo(x+450, y+123, x+388, y+114);
       context.quadraticCurveTo(x+357, y+144, x+303,y+ 115);
       context.quadraticCurveTo(x+251, y+118, x+278, y+83);
       context.quadraticCurveTo(x+254, y+46, x+320, y+46);
       context.quadraticCurveTo(x+326, y+12, x+362, y+37);
       context.closePath();
       context.stroke();
       context.fill();
       context.restore();    
    },

    //Painter
    ballPainter={
        paint:function(sprite,context){
            let x=sprite.left+sprite.width,
                y=sprite.top+sprite.height,
                width=sprite.width,
                height=sprite.height;

            context.save();
            Arc.drawRoundedRect(BALL_STROKE_STYLE,BALL_FILL_STYLES,
                                            x,y,width,height,CORNER_RADIUS)
            context.restore();
        }
    },


//Game over
    over=function(that){
        let highScore,overDisplay,
            highScores=game.getHighScores();
        // if(highScores.length===0&&score>highScores[0].score){//||
        //     showHighScores(that); //App 
        // }
        // else{
            //gameOverToast.style.display = 'inline';
            overDisplay='inline'; //change it to the top level
        // }
        gameOver=true;
        lastScore=score;
        score=0;
        return overDisplay;
    },



//Pause and Auto-pause Event handler...................................
    togglePaused=function(that){ //that object in jsx
        game.togglePaused();
        //pausedToast.style.display = game.paused ? 'inline' : 'none';
        let displayx=game.paused?'inline':'none';
    //Checking if game is paused and trigger paused
        if(game.paused){
            Event.trigger('GamePause');
        }else{
            Event.trigger('GameRecover');
        }
        that.setState({
            display:displayx
        });
        
    },

    pauseToastClickHandler=function(that){
        //this --> the object in JSX
        // that.setState({
        //     display:'none'
        // });
        togglePaused(that);
    }
;

// 离开浏览器 失去焦点时触发
function windowOnBlur(that){
   // console.log(loading,gameOver,game.paused);
    if(!loading && !gameOver&& !game.paused){
        togglePaused(that);
        let displayx=game.paused?'inline':'none';
        that.setState({  
            display:displayx
        });
    }
    //console.log('sss');
}

function windowOnFocus(that){
    if(game.paused&&!gameOver){
        togglePaused(that);
        let displayx=game.paused?'inline':'none';
        that.setState({
            display:displayx
        });
    }
    //console.log('eee');
}

//New Game..................................
//Actually this belongs to the over Compoent
function newGameClick(){
    let str='none';
    //因为setState是异步的，所以其实这里应该promise，等到
    //状态resolved才执行startNewGame()
    setTimeout(()=>{
        startNewGame();
         setTimeout(()=>{
            //这里异步执行
            resetSprite();      
          },500);
    },100);
    return str;
};

function startNewGame(){
    //highScoreParagraph.style.display = 'none';
    ////让sprite.color最快重置为[] ? 在endGame那里重置才对
    emptyArr();
    gameOver=false;
    livesLeft=1;
    score=0;
    deepCopyColor();  //copy from FILL_STYLES to leftColor
    initalColor();//initialize color
    createStages(); //create totalColor
    //更新分数牌
    Event.trigger('LoadScore');
    //重置sprite应该在ledgeColor更新之后！
    
}

//High Scores
//Change game display to show high scores when player
//beats the high score
let showHighScores=function(that){
        //highScore  //
        that.setState({
            highScoreDisplay:'inline'
        });
        //livesLeft
        updateHighScoreList(that);
        that.setState({
            highScoreName:0 //0 focus 1 lost focus
        });
    },

    updateHighScoreList=function(that){
        let el,
            highScores=game.getHighScores(),
            length=highScores.length,
            highScore;
            //listParent  not necessary
        //highScoreList.id = 'highScoreList'; // Set up CSS of list
        if(length>0){
            that.setState({
                preScoreDisplay:'block'
            });
            length=length>10?10:length;
            //render high score list
            that.setState({
                rank:highScores
            });

        }else{
            that.setState({
                preScoreDisplay:'none'
            });
        }
    },

    addScoreClickHandler=function(e){
        game.setHighScore({name:this.state.nameValue,score:lastScore});
        updateHighScoreList();
        this.setState({
            buttonDisable:true,
            nameValue:''
        });
    },

    newGameFromScoreClickHandler=function(e){
        this.setState({
            highScoreDisplay:'none'
        });
        startNewGame();
    },

    nameInputKeyUpHandler=function(e){
        if(this.state.nameValue.length>0){
            this.setState({
                buttonDisable:false
            });
        }
        else{
            this.setState({
                buttonDisable:true
            });
        }
    };

//Score Display
    function updateScore (that) {
        // body... 
        if(!loading&& game.lastScoreUpdate!==undefined){
            if(game.gameTime-game.lastScoreUpdate>1000){
                that.setState({
                    scoreDisplay:'inline'
                });
                score+=10;
                //scoreToast.innerHTML = score.toFixed(0);
                game.lastScoreUpdate=game.gameTime;
            }
        }else{
            game.lastScoreUpdate=game.gameTime;
        }
    }

//End game button
let clearHighScoresCheckHandler=function(e){
        if(this.state.clearAll){
            game.clearHighScores();
        }
    },
    //LoadScore handler
    loadScoreDisplayHandler=function(){
        let newStateObj={
            scoreDisplay:'inline',
            scoreText:realMyObj.length
        };
        loading=false;
        score=10;
        //console.log('loading Score');
        game.playSound('pop');     
        return newStateObj;
    }

//Util  Function
//需要异步改变这个颜色。
function generateRandom(Arr){
    //实际上是对数组下标的随机生成。 
    let colorIndex=Math.floor(Math.random()*Arr.length);
    return Arr[colorIndex];
}

function randomColorMerge(Arr1,Arr2){
    let newArr,tmpArr;
    tmpArr=Arr1.concat(Arr1);
    newArr=tmpArr.concat(Arr2);
    return newArr;
}

function initalColor(){
    let initial=generateRandom(leftColor);
    //spriteColor.push(initial);
    //if(ballSprite){
    console.log('inial');
        ballSprite.color.push(initial);
    //}
    for(var i=0;i<RECT_NUM;i++){//RECT_NUM*2
        ranColor.push(initial);
        backColor.push(initial);
        //更新ledgeColor
        currentLedgeColor.push(initial);
        stageColor.push(initial);
    }
    //realMyObj.push({color:initial,left:300});
}

// //Juding/compare color
// function compareColor(){
//     ballSprite.color.forEach((item)=>{

//     });
// }

function changeColor(){
    //let color=stageColor.shift();
    //let tmp=backColor.shift();
    copyToRan();
    //不要马上更新背景颜色，不然就会跟不上~
    //因为前面，动画可能还在画draw这个矩形
    setTimeout(()=>{
        updateBackColor();
    },50);
}

function updateBackColor(){
    for(let i=0;i<RECT_NUM;i++){
        let temp=stageColor.shift();
        backColor[i]=temp;
    }
}

function copyToRan(){
    for(let i=0;i<RECT_NUM;i++){
        ranColor[i]=backColor[i];
    }
}

//Using push ,not assign,because it's empty 
function deepCopyColor(){
    for(let i=0;i<FILL_STYLES.length;i++){
        leftColor.push(FILL_STYLES[i]);
    }
}

function createStages(){
    let i=0,initial;
    initial=ranColor[0];
    myColor.push(initial);
    //Rule of color !! 
    while(i<TOTAL_RECT){ //totalColor 150
        let len=leftColor.length;
        //len>1 not len>0
        if(i%10===0&&i>10&&len>1){
            let tmpColor=generateRandom(leftColor);
            let index=leftColor.indexOf(tmpColor);
            //delete this color
            leftColor.splice(index,1);
            myColor.push(tmpColor); //adding new color to myColor
            //ballSprite.color.push(tmpColor);
        }
        //let newArr=randomColorMerge(myColor,leftColor);
        let color;
        if(i%3==0&&len>0) //每隔2个就添加一个新颜色中的一个
             color=generateRandom(leftColor) ;
        else {
             color=generateRandom(myColor)
        }
        stageColor.push(color);
        i++;
    }
}

//handle Game event
function handleGameClick(){
    //限制点击的次数，只能两次
    if(ballSprite.tapTimes<2&&!game.Paused){
        ballSprite.freeze=false;
        Event.trigger('OneTap');
        ballSprite.tapTimes++;
    }
}
//lookup table 
function updateCurrentColor(translateOffset){
    switch(translateOffset){
        case 0: 
            for(let i=0;i<RECT_NUM;i++){
                currentLedgeColor[i]=ranColor[i];
            }
        break;
        case RECT_WIDTH:
            currentLedgeColor.shift();
            currentLedgeColor.push(backColor[0]);break;
        case RECT_WIDTH*2:
            currentLedgeColor.shift();
            currentLedgeColor.push(backColor[1]);break;
        case RECT_WIDTH*3:
            currentLedgeColor.shift();
            currentLedgeColor.push(backColor[2]);     
            break;
        case CANVAS_WIDTH:
            currentLedgeColor.shift();
            currentLedgeColor.push(backColor[3]);break;
        default:break;
    }
}

function updateLedgeColor(){
    //updating here 
    let tmpRect=Behavior.fallOnLedge.ledgeRect;
    for(let i=0;i<RECT_NUM;i++){
        tmpRect[i].color=currentLedgeColor[i];
    }
}

function updateLedgeLeft(translateDelta){
    let tmpRect=Behavior.fallOnLedge.ledgeRect;
   // resetLeft();
   // 有一定规律遇到颜色相同但是还是会falling，这是游戏故意
   // 设置的难度
    for(let i=0;i<RECT_NUM;i++){
        tmpRect[i].left=tmpRect[i].left-translateDelta;

    }
    //console.log(tmpRect[1].left);
}

function resetLeft(){
    let tmpRect=Behavior.fallOnLedge.ledgeRect;
    for(let i=0;i<RECT_NUM;i++){
        tmpRect[i].left=i*RECT_WIDTH;
    }
}

function endGame(){
    //livesLeft为0 和 gameOver为true后面不触发over
    //我们只有一条命
        livesLeft--;
        gameOver=true;
        score=0;
        countRect=0;
        ballSprite.color=[];
        Event.trigger('over');
       
}

function emptyArr(){
    realMyObj=[];
    stageColor=[];
    leftColor=[];
    myColor=[];
    ranColor=[];backColor=[];
    currentLedgeColor=[];
}

function resetSprite(){
    //ballSprite.color=[];
    ballSprite.velocityX=0;
    ballSprite.velocityY=0;
    ballSprite.tapTimes=0;
    ballSprite.left=BALL_LAUNCH_LEFT;
    ballSprite.top=BALL_LAUNCH_TOP;
    ballSprite.trap=false;
}

function updateMyRealColor(){ //countRect realMyColor myColor
    let leftOffset=calculLeftOffset();
    if(countRect%10===0&&countRect>=10){
        let color=myColor.shift();
        if(color){ //if color==undefined, then escape it 
            let tmpObj={color:color,left:leftOffset};
            realMyObj.push(tmpObj);
            //console.log(ballSprite.color);
            ballSprite.color.push(color);
            //触发更新分数,事件在ScoreToast中监听
            Event.trigger('LoadScore');
        }
        //realMyColor.push(color);//adding new color 
    }
    if(realMyObj.length<11){//if length >11
        Event.trigger('updateLineBallObj',realMyObj);
    }
}

function calculLeftOffset(){
    //len is changing all the time. leftOffset is a local variable
    let len=realMyObj.length,leftOffset;
    if(len===1){
        leftOffset=250;
        return leftOffset;
    }
    if(len<7){ //already draw 1?
        leftOffset=250-(len-1)*45;
    }else {
        leftOffset=250+(len-6)*45;
    }
    return leftOffset;
}

//context.canvas.onclick=handleGameClick;




//Load game ....................................................
//Running the game Here 
loading=true;
//Adding Sprite here
//Sprite  && behavior 
//Initalize 
let temp=Behavior.fallOnLedge.ledgeRect[0],temp2=Behavior.fallOnLedge.ledgeRect[1],
    temp3=Behavior.fallOnLedge.ledgeRect[2],temp4=Behavior.fallOnLedge.ledgeRect[3];

temp.top=temp2.top=temp3.top=temp4.top=game.context.canvas.height-RECT_HEIGHT;
temp.width=temp2.width=temp3.width=temp4.width=RECT_WIDTH;
temp.height=temp2.height=temp3.height=temp4.height=RECT_HEIGHT;
resetLeft();

let ballSprite=new Sprite('ball',ballPainter,[Behavior.moveGravity,Behavior.fallOnLedge]);
//INITALIZE
ballSprite.top=BALL_LAUNCH_TOP;
ballSprite.left=BALL_LAUNCH_LEFT;
ballSprite.width=BALL_WIDTH;
ballSprite.height=BALL_HEIGHT;
ballSprite.velocityX=10;
ballSprite.velocityY=0;
//console.log(Behavior.fallOnLedge);
game.addSprite(ballSprite);
ballSprite.color=[];
deepCopyColor();  //copy from FILL_STYLES to leftColor
initalColor();//initialize color
createStages(); //create totalColor stageColor
//console.log(stageColor);

//hadnler of Game Event 
context.canvas.addEventListener('click',handleGameClick);
Event.listen('EndGame',endGame);
Event.listen('GamePause',()=>{Behavior.PauseHandler(ballSprite)});
Event.listen('GameRecover',Behavior.RecoverHandler);
Event.listen('OneTap',TapHandler);

function TapHandler(){
    let now=+new Date();
    Behavior.startFalling(ballSprite);
    Behavior.moveGravity.lastFrameTime=now;
}

let loadButtonHandler=function(e){
    let interval,loadingPercentComplete=0;
    e.preventDefault();
    //Inside handler  don't use this! 
    this.setState({
        loadButtonDisplay:'none',
        loadMsgDisplay:'block',
        progressDivDisplay:'block'
    });
    //progressDiv.appendChild(progressbar.domElement);
    //No problem it's url of image , not physical path
    game.queueImage('/public/waterfall.png');
    game.queueImage('/public/tree.png');
    //Supposed to rewrite it with Promise!
    //Interval is the return value of setInterval 
    interval=setInterval((e)=>{
        loadingPercentComplete=game.loadImages();
        if(loadingPercentComplete===100){
            clearInterval(interval);
            setTimeout((e)=>{
                this.setState({
                    loadMsgDisplay:'none',
                    progressDivDisplay:'none'
                });
                setTimeout((e)=>{
                    this.setState({
                        loadingTitle:'none'
                    });
                    setTimeout((e)=>{
                        this.setState({
                            loadingToastDisplay:'none'
                        })
                        game.playSound('pop');
                        setTimeout((e)=>{
                            //Trigger the user-defined event. 
                            Event.trigger('LoadScore');
                            gameStart=true;
                            //console.log('here');
                        },50);
                    },10);
                },10);
            },10);
        }
         //progressbar.draw(loadingPercentComplete);
    },16)
}

//Game Paint Method override
game.startAnimate=function(){
    ballSprite.fps =game.fps;
    //console.log(ballSprite.color);
};
//called after thr sprites are painted
game.paintOverSprites=function(translateOffset){
    scrollBackground();
   // checkPausedAndTrigger();
};

//Called before the sprite is painted 
game.paintUnderSprites=function(){//Draw things other than sprites
    if(!gameOver&& livesLeft===0 ){///gameOver==false
        //over(that); //here call over 
        ///事件 观察者模式， 订阅over事件，因为这个需要在
        ///处理函数中调用 this.setState, 我选择了直接传入this来处理
        ///这样就需要在App.jsx 中，利用arrow function来处理，但是其实
        ///我不应该在业务逻辑直接this.setState来改变状态，而应该
        ///把状态改变了之后返回给 React组件，在React组件里面通过事件
        ///来获取这个改变后的state值，然后更新state。 其实也就是说
        ///我的React里面的某个事件会触发这个state改变，但是改变是在
        ///我业务逻辑中实现的 
        console.log('life ===0 but game not over');
        //Event.trigger('over');
    }else{
        paintSun(game.context);

        if(!gameOver){
            //updateScore(that); 
            Event.trigger('updateScore');
        }
    }
};

game.endAnimate=function(){

}


export {
    over,updateScore,
    togglePaused,pauseToastClickHandler,
    windowOnBlur,windowOnFocus,
    newGameClick,
    addScoreClickHandler,
    newGameFromScoreClickHandler,
    nameInputKeyUpHandler,
    clearHighScoresCheckHandler,
    loadButtonHandler,
    loadScoreDisplayHandler,
    startNewGame,
    gameOver,
    game
};

